package blacknWhite.Libraries;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.ArrayAdapter;
import blacknWhite.MarketSalesMonitor.PreferencesActivity;

public class CheckoutLibrary {
	static Lock lock = new ReentrantLock();

	// static String merchantId = "548484053261376";
	// static String merchantKey = "B84llAaQ7R6z-0bj6XZfmg";

	public static void refreshOrders(View progressBar,
			ArrayAdapter<View> dataAdapter, Context ctx) {
		AppDatabase mOpenHelper = null;
		Cursor cursor = null;
		try {
			mOpenHelper = new AppDatabase(ctx);
			final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
			cursor = db.query(AppDatabase.Tables.ORDERS, null, null, null,
					null, null, "sale_date DESC");
			Date startDate;
			if (cursor.moveToFirst()) {
				// Find the newest date and use it as start date
				Long saleDateLong = cursor.getLong(2);
				startDate = new Date(saleDateLong);
			} else {
				// If no newest date found use now minus 24 hours
				startDate = new Date();
				startDate.setHours(startDate.getHours() - 24);
			}
			Date endDate = new Date();
			endDate.setHours(endDate.getHours() + 1);

			if (progressBar != null)
				progressBar.setVisibility(View.VISIBLE);
			GetOrders(progressBar, ctx, dataAdapter, startDate, endDate);
		} catch (Throwable e) {
			notifyDataChange(progressBar, dataAdapter);
			Utils.LogException(e);
		} finally {
			if (cursor != null)
				cursor.close();
		}
	}

	private static void GetOrders(final View progressBar,
			final Context context, final ArrayAdapter<View> dataAdapter,
			Date startDate, Date endDate) {

		SimpleDateFormat dateFormatter = null;
		try {
			dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		} catch (Throwable ex) {
			Utils.LogException(ex);
		}

		final String startDateTxt = dateFormatter.format(startDate);
		final String endDateTxt = dateFormatter.format(endDate);
		final String timeZoneId = dateFormatter.getTimeZone().getID();

		new Thread(new Runnable() {
			public void run() {

				if (!lock.tryLock())
					return;
				try {
					String merchantId = getMerchantId(context);
					String merchantKey = getMerchantKey(context);

					if (merchantKey.contentEquals("")
							|| merchantKey.contentEquals("")) {
						final Runnable uiThread = new Runnable() {
							public void run() {
								context.startActivity(new Intent(context,
										PreferencesActivity.class));
							}
						};
						if (progressBar != null)
							Utils.handler.post(uiThread);
						return;
					}

					String commandXml = "<order-list-request xmlns=\"http://checkout.google.com/schema/2\" "
							+ "start-date=\""
							+ startDateTxt
							+ "\" end-date=\""
							+ endDateTxt
							+ "\">"
							+ "<date-time-zone>"
							+ timeZoneId
							+ "</date-time-zone>"
							+ "</order-list-request>";

					String reportsUrlFormatString = "https://checkout.google.com/api/checkout/v2/reports/Merchant/%s";
					String url = String.format(reportsUrlFormatString,
							merchantId);
					HttpURLConnection connection = makeConnection(context, url,
							merchantId, merchantKey);
					String response = postXML(connection, commandXml);

					ParseCommaDelimitedOrders(context, response);
				} catch (HttpUrlException e) {
					String response = e.getResponse();
					if (response != null) {
						int posStart = response.indexOf("<error-message>")
								+ "<error-message>".length();
						int posEnd = response.indexOf("</error-message>");

						if (posStart > 0 && posEnd > posStart) {
							final String errText = response.substring(posStart,
									posEnd);
							final Runnable refreshDataFromAsync = new Runnable() {

								public void run() {
									Utils.showToast(context, errText);
									context.startActivity(new Intent(context,
											PreferencesActivity.class));
								}
							};
							if (progressBar != null)
								Utils.handler.post(refreshDataFromAsync);
						}
					}
				} catch (Throwable ex) {
					Utils.LogException(ex);
				} finally {
					lock.unlock();
					notifyDataChange(progressBar, dataAdapter);
				}
			}
		}).start();
	}

	// / <summary>
	// / Parse the given comma-delimited file.
	// / </summary>
	// / <param name="FileName">Location of the file.</param>
	public static void ParseCommaDelimitedOrders(Context context, String data) {
		AppDatabase mOpenHelper = null;

		mOpenHelper = new AppDatabase(context);
		final SQLiteDatabase db = mOpenHelper.getWritableDatabase();

		try {

			String[] fileLines = SplitTextByLine(data);
			for (int index = 1; index < fileLines.length; index++) {
				String[] items = fileLines[index].trim().split(",");
				// Google Order Number (0),Merchant Order Number (1),Order
				// Creation
				// Date (2),
				// Currency of Transaction (3),Order Amount (4),Amount Charged
				// (5),Financial Status (6),
				// Fulfillment Status (7)

				if (items.length > 1) {
					try {
						// [305428300145450, 305428300145450,
						// "May 1,  2010 1:00:14 AM", USD, 3.99, 3.99, CHARGED,
						// DELIVERED]
						int currItem = 0;
						String orderNumber = items[currItem];
						String currency;
						double total;
						String status;

						String dateText;
						if (items[2].startsWith("\"")
								&& !items[2].endsWith("\"")
								& !items[3].startsWith("\"")
								&& items[3].endsWith("\"")) {
							dateText = items[2].substring(1)
									+ items[3].substring(0,
											items[3].length() - 1);
							currency = items[4];
							total = Double.parseDouble(items[5]);
							status = items[7];
						} else {
							dateText = items[2];
							currency = items[3];
							total = Double.parseDouble(items[4]);
							status = items[6];
						}

						Date saleDate = null;
						SimpleDateFormat dateFormatter = new SimpleDateFormat(
								STANDARD_DATE_FORMAT);
						try {
							saleDate = dateFormatter.parse(dateText);
						} catch (Throwable e) {
							dateFormatter = new SimpleDateFormat(
									ALTERNATE_DATE_FORMAT);
							saleDate = dateFormatter.parse(dateText);
						}
						if (saleDate == null)
							saleDate = new Date();

						AddOrder(db, orderNumber, saleDate.getTime(), currency,
								total, status);
					} catch (Throwable e) {
						Utils.LogException(e);
					}
				}
			}
		} finally {
			if (db != null && db.isOpen())
				db.close();

		}
	}

	public static final String STANDARD_DATE_FORMAT = "MMM dd yyyy h:mm:ss a";
	public static final String ALTERNATE_DATE_FORMAT = "dd-MMM-yyyy HH:mm:ss";

	public static void AddOrder(SQLiteDatabase db, String orderNumber,
			long saleDate, String currency, double total, String status) {
		ContentValues cv = new ContentValues();
		cv.put(AppDatabase.OrdersColumns.ORDER_NUMBER, orderNumber);
		cv.put(AppDatabase.OrdersColumns.SALE_DATE, saleDate);
		cv.put(AppDatabase.OrdersColumns.CURRENCY, currency);
		cv.put(AppDatabase.OrdersColumns.TOTAL, total);
		cv.put(AppDatabase.OrdersColumns.STATUS, status);
		try {
			db.insertOrThrow(AppDatabase.Tables.ORDERS, null, cv);
		} catch (Throwable e) {
			Utils.LogException(e);
		}
	}

	// / <summary>
	// / Read the contents of the text and return an array of lines
	// / </summary>
	public static String[] SplitTextByLine(String text) {
		return text.replace("\r\n", "\n").split("\n");
	}

	public static String getMerchantId(final Context ctx) {
		SharedPreferences sp = PreferenceManager
				.getDefaultSharedPreferences(ctx);

		String merchantId = sp.getString("merchantId", "");
		return merchantId;
	}

	/**
	 * @return The merchantKey as passed in the constructor.
	 */
	public static String getMerchantKey(final Context ctx) {
		SharedPreferences sp = PreferenceManager
				.getDefaultSharedPreferences(ctx);

		String merchantKey = sp.getString("merchantKey", "");
		return merchantKey;
	}

	public static String getHttpAuth(Context context, String merchantId,
			String merchantKey) {
		return "Basic "
				+ Base64Coder.encode(new StringBuffer(merchantId).append(":")
						.append(merchantKey).toString());
	}

	public static HttpURLConnection makeConnection(Context context,
			String toUrl, String merchantId, String merchantKey)
			throws CheckoutException {
		HttpURLConnection connection = openHttpConnection(toUrl);
		connection.setDoInput(true);
		connection.setDoOutput(true);
		connection.setReadTimeout(0);
		connection.setInstanceFollowRedirects(true);
		connection.setRequestProperty("Authorization", getHttpAuth(context,
				merchantId, merchantKey));
		try {
			connection.setRequestMethod("POST");
		} catch (ProtocolException e) {
			throw new CheckoutException(e);
		}
		return connection;
	}

	protected static HttpURLConnection openHttpConnection(String toUrl)
			throws CheckoutException {
		URL url;
		try {
			url = new URL(toUrl);
			return (HttpURLConnection) url.openConnection();
		} catch (IOException e) {
			throw new CheckoutException(e);
		}
	}

	public static String postXML(HttpURLConnection socket, String xmlString) {
		try {
			Exception underlying = null;
			try {
				Writer writer = new OutputStreamWriter(socket.getOutputStream());
				writer.write(xmlString);
				writer.close();
				if (socket.getResponseCode() == 200) {
					String slurp = slurp(socket.getInputStream());
					// logger.log(Level.INFO,
					// "Received from:\t{0}\nContent:\t{1}",
					// new Object[]{socket.getURL(), slurp});
					return slurp;
				}
			} catch (IOException e) {
				underlying = e;
			}
			throw makeUrlException(socket, xmlString, underlying);
		} finally {
			socket.disconnect();
		}
	}

	private static HttpUrlException makeUrlException(HttpURLConnection hurlc,
			Object triedToSend, Throwable possibleUnderlying) {
		URL url = hurlc.getURL();
		int responseCode;
		String slurp;
		try {
			slurp = slurp(hurlc.getErrorStream());
		} catch (IOException e) {
			if (possibleUnderlying != null) {
				// logger.log(Level.SEVERE,
				// "Masking exception while processing communication error from checkout:",
				// e);
			} else {
				possibleUnderlying = e;
			}
			slurp = "<Error reading cause of error>";
		}
		try {
			responseCode = hurlc.getResponseCode();
		} catch (IOException e) {
			if (possibleUnderlying != null) {
				// logger.log(Level.SEVERE,
				// "Masking exception while processing communication error from checkout:",
				// e);
			} else {
				possibleUnderlying = e;
			}
			responseCode = Integer.MAX_VALUE;
		}
		return new HttpUrlException(url, responseCode, triedToSend, slurp,
				possibleUnderlying);
	}

	public static String slurp(InputStream from) throws IOException {
		if (from == null) {
			return "";
		}
		final char[] buffer = new char[1024];
		StringBuilder out = new StringBuilder();
		Reader in = new InputStreamReader(from, "UTF-8");
		int read;
		do {
			read = in.read(buffer, 0, buffer.length);
			if (read > 0) {
				out.append(buffer, 0, read);
			}
		} while (read >= 0);
		return out.toString();
	}

	private static void notifyDataChange(final View progressBar,
			final ArrayAdapter<View> dataAdapter) {
		final Runnable refreshDataFromAsync = new Runnable() {

			public void run() {
				if (progressBar != null)
					progressBar.setVisibility(View.GONE);
				if (dataAdapter != null)
					dataAdapter.notifyDataSetChanged();
			}
		};
		Utils.handler.post(refreshDataFromAsync);
	}

}
